
/*    This file is part of the Managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.managarm.jmanavalib.core;

public abstract class VaDatatype extends VaObject {
	// returns true if this type is compatible to "other"
	public abstract boolean compatible(VaDatatype other);
	
	// builds a hash code based on fields significant for compatibility
	public abstract int compatibilityHash();
	
	public static final class DtPrimitive extends VaDatatype {
		// primitive type constants
		public static final short PRIM_UBYTE = 1;
		public static final short PRIM_USHORT = 2;
		public static final short PRIM_UINT = 3;
		public static final short PRIM_ULONG = 4;
		public static final short PRIM_BYTE = 5;
		public static final short PRIM_SHORT = 6;
		public static final short PRIM_INT = 7;
		public static final short PRIM_LONG = 8;
		public static final short PRIM_NULL = 9;
		public static final short PRIM_CHAR = 10;
		public static final short PRIM_BOOLEAN = 11;

		public final short identity;

		public DtPrimitive(short identity) {
			this.identity = identity;
		}
		
		@Override public boolean compatible(VaDatatype other) {
			if(other instanceof DtPrimitive == false)
				return false;
			final DtPrimitive type = (DtPrimitive)other;
			return identity == type.identity;
		}
		
		@Override public int compatibilityHash() {
			return 17 + 17 * 37 + identity;
		}
		
		@Override public String toString() {
			if(identity == PRIM_UBYTE) {
				return "ubyte";
			}else if(identity == PRIM_USHORT) {
				return "ushort";
			}else if(identity == PRIM_UINT) {
				return "uint";
			}else if(identity == PRIM_BYTE) {
				return "byte";
			}else if(identity == PRIM_SHORT) {
				return "short";
			}else if(identity == PRIM_UINT) {
				return "uint";
			}else if(identity == PRIM_CHAR) {
				return "char";
			}else if(identity == PRIM_BOOLEAN) {
				return "boolean";
			}else{
				return "<Primitive: " + identity + ">";
			}
		}
		
		@Override final public int hashCode() {
			int hash = 17;
			hash = 37 * hash + identity;
			return hash;
		}
		
		@Override final public boolean equals(final Object object) {
			if(object instanceof DtPrimitive == false)
				return false;
			final DtPrimitive other = (DtPrimitive)object;
			if(identity != other.identity)
				return false;
			return true;
		}
	}
	
	public static final class DtClass extends VaDatatype {
		public static final short FLAG0_XNAT_REFCOUNTED = 256;
		
		// class description for this type
		public final VaClsInfo clsInfo;
		
		public DtClass(VaClsInfo clsinfo) {
			this.clsInfo = clsinfo;
		}
		
		@Override public boolean compatible(VaDatatype other) {
			if(other instanceof DtClass == false)
				return false;
			final DtClass type = (DtClass)other;
			return clsInfo.compatible(type.clsInfo);
		}
		
		@Override public int compatibilityHash() {
			return 17 * 37 + clsInfo.compatibilityHash();
		}
		
		@Override public boolean equals(final Object object) {
			if(object instanceof DtClass == false)
				return false;
			final DtClass other = (DtClass)object;
			return clsInfo.equals(other.clsInfo);
		}
		
		@Override public int hashCode() {
			return 17 * 37 + clsInfo.hashCode();
		}
		
		@Override public String toString() {
			return clsInfo.ident;
		}
	}
	
	public static final class DtArray extends VaDatatype {
		public static final short FLAG0_LOCAL = 1;
		public static final short FLAG0_UNMANAGED = 2;
		public static final short FLAG0_XNAT_REFCOUNTED = 256;
		
		// base type of this array
		public final VaDatatype basetype;
		
		// flags of this array
		public final int flags0;
		
		public DtArray(VaDatatype basetype) {
			this(basetype, 0);
		}
		public DtArray(VaDatatype basetype, int flags0) {
			this.basetype = basetype;
			this.flags0 = flags0;
		}
		
		@Override public boolean compatible(VaDatatype other) {
			if(other instanceof DtArray == false)
				return false;
			final DtArray type = (DtArray)other;
			if((flags0 & FLAG0_UNMANAGED)
					!= (type.flags0 & FLAG0_UNMANAGED))
				return false;
			if((flags0 & FLAG0_XNAT_REFCOUNTED)
					!= (type.flags0 & FLAG0_XNAT_REFCOUNTED))
				return false;
			return basetype.compatible(type.basetype);
		}
		
		@Override public int compatibilityHash() {
			int hash = 17;
			hash = 37 * hash + basetype.compatibilityHash();
			hash = 37 * hash + ((flags0 & FLAG0_UNMANAGED) != 0 ? 0 : 1);
			hash = 37 * hash + ((flags0 & FLAG0_XNAT_REFCOUNTED) != 0 ? 0 : 1);
			return hash;
		}
		
		@Override public boolean equals(Object object) {
			if(object instanceof DtArray == false)
				return false;
			final DtArray other = (DtArray)object;
			if((flags0 & FLAG0_UNMANAGED)
					!= (other.flags0 & FLAG0_UNMANAGED))
				return false;
			if((flags0 & FLAG0_XNAT_REFCOUNTED)
					!= (other.flags0 & FLAG0_XNAT_REFCOUNTED))
				return false;
			return basetype.equals(other.basetype);
		}
		
		@Override public int hashCode() {
			int hash = 17;
			hash = 37 * hash + basetype.hashCode();
			hash = 37 * hash + ((flags0 & FLAG0_UNMANAGED) != 0 ? 0 : 1);
			hash = 37 * hash + ((flags0 & FLAG0_XNAT_REFCOUNTED) != 0 ? 0 : 1);
			return hash;
		}
		
		@Override public String toString() {
			if((flags0 & FLAG0_LOCAL) != 0)
				return "local$" + basetype.toString() + "[]";
			if((flags0 & FLAG0_XNAT_REFCOUNTED) != 0)
				return "refcnt$" + basetype.toString() + "[]";
			return basetype.toString() + "[]";
		}
	}

	public static final class DtDelegate extends VaDatatype {
		// return type of this delegate
		public final VaDatatype returnType;
		
		// argument types of this delegate
		private final VaDatatype[] arguments;
		
		public DtDelegate(VaDatatype returntype,
				VaDatatype[] arguments) {
			this.returnType = returntype;
			this.arguments = arguments.clone();
		}
		
		// returns the argument count
		public final int getArgumentCnt() {
			return arguments.length;
		}
		
		// returns the argument type of the argument at "index"
		public final VaDatatype getArgument(int index) {
			return arguments[index];
		}	
		
		@Override public boolean compatible(VaDatatype other) {
			if(other instanceof DtDelegate == false)
				return false;
			final DtDelegate type = (DtDelegate)other;
			if(!returnType.compatible(type.returnType))
				return false;
			if(arguments.length != type.arguments.length)
				return false;
			for(int i = 0; i < arguments.length; i++)
				if(!arguments[i].compatible(type.arguments[i]))
					return false;
			return true;
		}
		
		@Override public int compatibilityHash() {
			int hash = 17;
			hash = 37 * hash + returnType.compatibilityHash();
			for(int i = 0; i < arguments.length; i++)
				hash = 37 * hash + arguments[i].compatibilityHash();
			return hash;
		}
		
		@Override public boolean equals(Object object) {
			if(object instanceof DtDelegate == false)
				return false;
			final DtDelegate other = (DtDelegate)object;
			if(!returnType.compatible(other.returnType))
				return false;
			if(arguments.length != other.arguments.length)
				return false;
			for(int i = 0; i < arguments.length; i++)
				if(!arguments[i].compatible(other.arguments[i]))
					return false;
			return true;
		}
		
		@Override public int hashCode() {
			int hash = 17;
			hash = 37 * hash + returnType.hashCode();
			for(int i = 0; i < arguments.length; i++)
				hash = 37 * hash + arguments[i].hashCode();
			return hash;
		}
	}
}
